#!/usr/bin/python2.5
"""

    body.py
    [--log_file PATH]
    [--quiet]

"""

#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   options        


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

from optparse import OptionParser
import sys, os
import os.path
import StringIO

# add self to search path for testing
if __name__ == '__main__':
    exe_path = os.path.split(os.path.abspath(sys.argv[0]))[0]
    sys.path.append(os.path.abspath(os.path.join(exe_path,"..", "python_modules")))
    myname = os.path.split(sys.argv[0])[1]
    myname = os.path.splitext(myname)[0];
else:
    myname = __name__




parser = OptionParser(version="%prog 1.0", usage = "usage: %prog [options] FILE")
parser.add_option("-f", "--from", dest = "beg_pos",
                  metavar="N", 
                  type="int",
                  help="Print starting at N lines or bytes. Use negative numbers "
                        "to count from file end. E.g. N = -1 returns last line / byte.")
parser.add_option("-l", "--length", dest="length",
                  metavar="N", 
                  type="int",
                  help="Print N lines or bytes from FILE. Set to 0 for rest of file")
parser.add_option("-b", "--bytes", dest="bytes",
                    action="store_true", default=False,
                    help="Specify bytes instead of numbers of lines.")
parser.add_option("-v", "--verbose", dest="verbose",
                    action="store_true", default=False,
                    help="Show progress.")

parameters = [  
                "length",
                "beg_pos",
                ]


parser.set_defaults(begin_pos   = 0)
parser.set_defaults(length      = 1)


# get help string
import StringIO
f =StringIO.StringIO()
parser.print_help(f)
helpstr = f.getvalue()

# check file name specified
(options, remaining_args) = parser.parse_args()
if len(remaining_args) !=  1:
    sys.stderr.write("Error\n:\tPlease specify a file\n" + helpstr)
    sys.exit(1)

file_name = remaining_args[0];


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   imports        


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888



#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Functions        


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
def nth(n, searchString, buffer):
    pieces = buffer.split(searchString, n)
    if len(pieces) != n+1: return -1
    return len(buffer)-len(pieces[n])-len(searchString)


import mmap
def file_to_mmap (infile):
    try:
        return mmap.mmap(infile.fileno(), 0, mmap.MAP_PRIVATE, mmap.PROT_READ)
    # not enough memory
    except EnvironmentError:
        sys.stderr.write("File too large: Could not map file\n")
        return infile

import os
def get_pos_of_nth_line_from_back(infile, file_size, nth_from_back):

    infile.seek(-1, 2)
    if infile.read(1) == "\n":
        nth_from_back += 1
    # chunk size
    #chunk_size = 8192 * 1024
    chunk_size = 10
    pos_from_end = min(chunk_size, file_size)
    cnt_line_ends = 0
    while pos_from_end <= file_size:
        infile.seek(-pos_from_end, 2)
        buf = infile.read(chunk_size)
        cnt_line_ends += buf.count("\n")
        if cnt_line_ends >= nth_from_back:
            offset = nth(cnt_line_ends - nth_from_back + 1, "\n", buf)
            return file_size - pos_from_end + offset + 1
        if pos_from_end == file_size:
            return 0
        pos_from_end += chunk_size
        pos_from_end == min(file_size, pos_from_end)



#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Main logic


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

# get file_size
import os
file_size = os.path.getsize(file_name)

infile = open(file_name)
if options.verbose:
    sys.stderr.write("[")


if options.bytes == True:
    beg_pos = options.beg_pos
    if (beg_pos < 0):
        beg_pos = file_size + options.beg_pos + 1 - options.length
    infile.seek(beg_pos)
    sys.stdout.write(infile.read(options.length))
        
else:
    if options.beg_pos >= 0:
        for line_count, line in enumerate(infile):
            if options.verbose and line_count % 10000 ==0:
                sys.stderr.write(".")
    
            #
            #   ignore first xxx lines
            #
            if line_count < options.beg_pos:
                continue
    
            if line_count < options.beg_pos + options.length:
                sys.stdout.write(line)
        #
        #   too few lines to print anything 
        #
        if line_count < options.beg_pos + options.length:
            sys.stderr.write("File was only %d lines long\n" % line_count)

    #
    #   count from back 
    #
    else:
        infile = file_to_mmap (infile)
        #
        #   start position from end
        #
        beg_pos = get_pos_of_nth_line_from_back(infile, file_size, options.length + (- 1 - options.beg_pos))
        end_pos = file_size
        if options.beg_pos != -1:
            end_pos = get_pos_of_nth_line_from_back(infile, file_size, (- 1 - options.beg_pos))
        if beg_pos < end_pos:
            infile.seek(beg_pos)
            sys.stdout.write(infile.read(end_pos - beg_pos + 1))
        
        
if options.verbose:
    sys.stderr.write("]\n")
    
    
    
